<?php
/*----------------------------------------------------------------------------------------------
Kit Applicatif PHP
Noyau générique d'applications PHP / generic kernel for PHP applications
noyau 0.4 / PHP 4
http://code.fredbird.org/kap

gestion et stockage de connexions authentifiées, stockage sql
// dérivée de la classe authentifications

file version 2005-02-15 15:09:04
----------------------------------------------------------------------------------------------*/

class kap_connexionManager extends kap_core_component {
/*----------------------------------------------------------------------------------------------
                                                                           parametres par defaut
----------------------------------------------------------------------------------------------*/
	var $default_parameters=array(
		'maxLength' => 86400, // duree maximale, en secondes
		'useAutoDeconnection' => false, // deconnexion auto en cas d'inactivite
		'autoDeconnectionDelay' => 1800, // en secondes
		'useMD5checkSum' => true, // usage de la somme de controle md5 ajoutee a l'identifiant
		'allowIpChange' => true, // permettre le changement d'ip en cours de session
		'maxLivePerUser' => 0, // nbre maximal de connections simultanées ouvrables par utilisateur
		// structure de la table
		'tableName' => "kap_connexions",
		'idField' => "id",
		'ipField' => "ip",
		'uriField'=>'uri',
		'userIdField' => "user_id",
		'startDateField' => "date_debut",
		'limitDateField' => "date_peremption",
		'closeDateField' => "date_fermeture"
	);
	var $isLogged=false; // etat actuel
	var $timeLeft; // duree de validite restante
	var $whyUnlogged; /* raison de non connexion
		no_session : pas de session cree, pas de clef trouvee
		opening_failed : echec d'ouverture
		no_key : pas de clef trouvee en cookie ni session
		invalid_key : clef introuvable dans la table
		invalid_ip : l'ip a changé
		invalid_checksum : somme de controle md5 invalide
		too_much_attempts : trop de tentatives dans le delai imparti
		no_more_connection : l'utilisateur a deja ouvert son quota de connexions
		sql_error : erreur lors d'un connexion mysql
		session_closed : session en cours ferme manuellement
		session_outdated : session perime (inactivite detectee ou dure max atteinte)
/*----------------------------------------------------------------------------------------------
                                                                           constructeur eventuel
----------------------------------------------------------------------------------------------*/
	/*function kap_(& $controller) {
		// appel du constructeur parent
		$const=get_parent_class($this);
		parent::$const($controller);
		// suite
	}*/
/*----------------------------------------------------------------------------------------------
                                                                              methodes publiques
a lier dans components/controller.kap-core.php et api.kap-core.php
----------------------------------------------------------------------------------------------*/
	// crée une session, renvoie sa clef le cas échéant (sinon false)
	function open($user_id) {
		//echo $user_id;
		// verification que l'utilisateur n'est pas déjà connecté
		if ($max=$this->parameters['maxLivePerUser']) {
			$alive=$this->countAliveConnections($user_id);
			kapLog("connexionManager: user already opened $alive connections, max is $max.");
			$this->whyUnlogged='no_more_connection';
			if ($alive>=$max) return false;
		}
		// insertion mysql
		if (!$this->insert($user_id)) return false;
		// definition de la clef => via insert()
		/*if ($this->parameters['useMD5checkSum']) {
			$this->key=$this->sessionId.$this->checkSum;
		} else {
			$this->key=$this->sessionId;
		}*/
		//echo affvar($this->key);
		// renseignement des infos de session interne
		
		$this->isLogged=true;
		return $this->key;
	}
	// recuperation de la clef de connexion actuelle
	function getKey() {
		if (!$this->isLogged) return false;
		return $this->key;
	}
	// recuperation du temps de validité restante
	function getTimeLeft() {
		if (!$this->isLogged) return false;
		return $this->timeLeft;
	}
	// verification de l'existence d'une session ouverte
	// prolongation de la session si la deco auto est active
	// renvoie false, ou un tableau d'informations sur la session
	function check($key) {
		//echo "checking key $key";
		$this->isLogged=false;
		$id=substr($key,0,32);
		$checkSum=substr($key,32,32);
		// recherche sql
		$sql="SELECT "
		.$this->parameters['ipField']." AS ip"
		.", ".$this->parameters['userIdField']." AS user_id"
		.", ".$this->parameters['startDateField']." AS date_debut"
		.", ".$this->parameters['limitDateField']." AS date_peremption"
		.", ".$this->parameters['closeDateField']." AS date_fermeture"
		." FROM ".$this->parameters['tableName']
		." WHERE ".$this->parameters['idField']."='$id'";
		//echo $sql;
		if (!$session=kapSqlUniqueResult($sql)) {
			// session non trouvee
			$this->whyUnlogged='invalid_key';
			kapLog("connexionManager: invalid key, $key");
			return false;
		}
		// renseignement des infos de session interne
		$this->session=$session;
		//echo affvar($session);
		// verif checksum
		if ($this->parameters['useMD5checkSum']) {
			//$toCheck=$id.$session['ip'].$session['user_id'].$session['date_debut'];
			$toCheck=$this->makeCheckSum($id,$session['user_id'],$session['date_debut'],$session['ip']);
			if ($checkSum!=$toCheck) {
				//$this->is_logged=false;
				//echo $checkSum."!=".md5($toCheck);
				$this->whyUnlogged='invalid_checksum';
				kapLog("connexionManager: invalid key checksum, $key");
				return false;
			}
		}
		// verif ip => deja fait par verif checksum
		/*if ($this->parameters['allowIpChange']==false and $session['ip']!=$this->getIp()) {
			$this->whyUnlogged='invalid_ip';
			kapLog("connexionManager: invalid ip, ".$this->getIp());
			return false;
		}*/
		// verif fermeture
		if ($session['date_fermeture']!=NULL) {
			$this->whyUnlogged='session_closed';
			kapLog("connexionManager: session was closed at ".$session['date_fermeture']);
			return false;
		}
		// verif peremption
		if (date('Y-m-d H:i:s')>$session['date_peremption']) {
			$this->whyUnlogged='session_outdated';
			kapLog("connexionManager: session outdated at ".$session['date_peremption']);
			return false;
		}
		// session valide
		kapLog("connexionManager: session valid, key $key");
		// rafraichissement de la date de peremption si necessaire
		if ($this->parameters['useAutoDeconnection']) {
			$this->refreshSession($id);
			// remise a jour de la duree de validite
			$this->session['date_peremption']=date('Y-m-d H:i:s',$this->makeTimeLeft());
		}
		$this->isLogged=true;
		return $this->session;
	}
	
	// fermeture de la session en cours
	function close($id) {
		// coupure de la partie checksum eventuelle
		$id=substr($id,0,32);
		$sql="UPDATE ".$this->parameters['tableName']." SET "
		.$this->parameters['closeDateField']."=NOW()"
		." WHERE ".$this->parameters['idField']."='$id'"
		." AND ".$this->parameters['closeDateField']." IS NULL"
		." LIMIT 1";
		// execution
		$this->isLogged=!kapSqlExecute($sql);
		if (!$this->isLogged) {
			$this->whyUnlogged='session_closed';
			kapLog("connexionManager: closed connexion $id");
		} else {
			kapLog("connexionManager: failed to invalidate connexion $id");
		}
		return !$this->isLogged;
	}
	
	// renvoie l'id de l'utilisateur connectÚ
	function getUserId() {
		if (!$this->isLogged) return false;
		return $this->session['user_id'];
	}
	
	// renvoie les infos de session
	function getSessionInfos() {
		if (!$this->isLogged) return false;
		return $this->session;
	}
	
	function whyUnlogged() {
		return $this->whyUnlogged;
	}
	
	function install() {
		kapSqlOpen();
		// creation de la table
		$sql=kapFileContent(KAP_CORE_PATH.'data/kap_connexions.table-structure.sql');
		if (!kapSqlExecute($sql)) return false;
	}
	
/*-------------------------------------------------------------------------
																			methodes privees
-------------------------------------------------------------------------*/	

	// execute une requete sql	
	/*function _sql($sql) {
		if ($back=@mysql_query($sql)) return $back;
		trigger_error(Mysql_error()." ".$sql,E_USER_NOTICE);
		$this->whyUnlogged='sql_error';
		return false;
	}*/
	
	// creation d'un enregistrement dans la table identifications
	function insert($user_id) {
		// creation de la clef
		$this->sessionId=$this->createId();
		$ip=$this->getIp();
		$debut=date('Y-m-d H:i:s');
		$this->timeLeft=$this->makeTimeLeft();
		$fin=date('Y-m-d H:i:s',$this->timeLeft);
		// calcul de la somme de controle md5 si nécessaire
		if ($this->parameters['useMD5checkSum']) {
			
			//$this->checkSum=md5($this->sessionId.$ip.$user_id.$debut);
			$this->checkSum=$this->makeCheckSum($this->sessionId,$user_id,$debut);
			$this->key=$this->sessionId.$this->checkSum;
		} else $this->key=$this->sessionId;
		
		$sql="INSERT INTO ".$this->parameters['tableName']
		."(".$this->parameters['idField']
		.", ".$this->parameters['ipField']
		.", ".$this->parameters['uriField']
		.", ".$this->parameters['userIdField']
		.", ".$this->parameters['startDateField']
		.", ".$this->parameters['limitDateField'].")"
		." VALUES('".$this->sessionId
		."', '".$ip
		."', ".kapSQLSecureValue(CALL_URI)
		.", '".$user_id
		."', '".$debut
		."', '".$fin."')";// echo $sql;
		if (!kapSqlExecute($sql)) return false;
		$this->session=array(
			'id' => $this->sessionId,
			'user_id' => $user_id,
			'ip' => $ip,
			'date_debut' => $debut,
			'date_peremption' => $fin,
			'date_fermeture' => NULL
		);
		return true;
	}
	
	function createId() {
		return md5(uniqid('',true));
	}
	
	function getIp() {
		return $_SERVER['REMOTE_ADDR'];
	}
	
	/* retourne la duree de validite restante
	= now() + duree max
	= now() + autoDecoDelay si l'auto deco est active
	*/
	function makeTimeLeft() {
		if (!$this->parameters['useAutoDeconnection']) {
			return time()+$this->parameters['maxLength'];
		} else {
			return time()+$this->parameters['autoDeconnectionDelay'];
		}	
	}
	
	// prolongation de la duree de validite de la session
	function refreshSession($id) {
		$sql="UPDATE ".$this->parameters['tableName']." SET "
		.$this->parameters['limitDateField']."='".date('Y-m-d H:i:s',$this->makeTimeLeft())."'"
		." WHERE ".$this->parameters['idField']."='$id' LIMIT 1";
		return kapSqlExecute($sql);
	}
	
	function makeCheckSum($sessionId,$userId,$dateDebut,$ip=IP) {
		$string=$sessionId.$userId.$dateDebut;
		if (!$this->parameters['allowIpChange']) $string.=$ip;
		$checksum=md5($string);
		//echo $string.'=>'.$checksum;
		return $checksum;
	}
	
	function countAliveConnections($userId) {
		$sql="SELECT COUNT(*) AS alive"
		." FROM kap_connexions"
		." WHERE user_id=".kapSqlSecureValue($userId)
		." AND date_peremption>NOW() AND date_fermeture IS NULL";
		if (!$result=kapSqlUniqueResult($sql)) return 0;// echo affvar($result);
		return intval($result['alive']);
	}
/*----------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------*/
}
?>
